package algo_lignes_sites.data.pre_trt.simplif_node;

import algo_lignes_sites.data.junction.A_Junction.TypeJunction;
import algo_lignes_sites.data.junction.JunctionREF;
import algo_lignes_sites.data.path.A_Path;
import algo_lignes_sites.data.path.PathREF;
import algo_lignes_sites.params.Params_ls;
import commons.logs.Logger;
import commons.params_appli.ParamsAppli;
import commons.spatialindex.simplegrid.SimpleGridSPI;
import commons.utils.UtilsGeom;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import commons.utils.primitive_geom.Segment_d;
import gnu.trove.set.hash.THashSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;


// INFO : ZS = zone de simplification = cercle de centre le node et de rayon le rayon de simplification

public class SimplifNode {

	static private double largestRayon;
	static double[] tabCoord = new double[4]; // pour calculs d'intersection cercle / segment
	
	static ArrayList<JunctionREF> vJunctionREF;
	
	static double facteurReductionZSsiConflit;
	

	
	static public void simplifyInNodes(Params_ls params, ArrayList<PathREF> vPathREF, ArrayList<JunctionREF> vJunctionREF, ArrayList<JunctionREF> vNodeREF,
			                           SimpleGridSPI spiPath, SimpleGridSPI spiNode) {
		Logger.log_INFO1("-- Simplification dans les nodes --");
		
		if (!params.bSimplifNode) {
			Logger.log_INFO2("  -> fonction désactivée par l'utilisateur");
			return;
		}
		
		// Amelioration V18
		// Abandonné car trop complexe a parametrer
//		if (params.bUseSimplifAdaptativeNode)
//			Logger.log_INFO2(" -> utilisation de la simplification adaptative seuil complexité = " + params.seuilPourReductionDistSimplifNode);

		spiPath.clear();
		spiNode.clear();
		spiPath.addObjects (vPathREF);
		spiNode.addObjects(vNodeREF);
		
		SimplifNode.vJunctionREF = vJunctionREF;
		
		SimplifNode.facteurReductionZSsiConflit = ParamsAppli.getParam_d("LigneSite.FACTEUR_REDUCTION_ZONE_SIMPLIFICATION_NODE");
		
		
		// On détermine pour chaque node son rayon de simplification souhaité
		computeRayonSimplifNodes(params, vNodeREF, spiPath, spiNode);
		
		
				
		// Amélioration V20 13/04/2013 [DEBUT]
		// On recense les nodes qui risquent d'avoir des intersections parasites entre les path connecté au node (et simplifiés en ligne droite)
		//  et les path non connectés au poste qui passent tout près
		// 1ere étape, on recense dans une structure les nodes potentiellement concernés
		// On doit avoir d'abord calculé les rayons des ZS !
		recenseNodeAvecPathNonConnecte(vNodeREF, vPathREF, spiPath, true);
		

		
		// set contenant tous les Path qui devront etre supprimés en fin de traitement, car à l'intérieur complétement de ZS
		THashSet<PathREF> setPathtoSuppr = new THashSet<PathREF>(vPathREF.size()/10);
			
		// Traitement de chaque node
		for (JunctionREF node : vNodeREF)
			simplifyNode(node, setPathtoSuppr, spiPath);
		
		// Suppression effective des path à supprimer
		Logger.log_INFO2(" -> nb de Path supprimés = " + setPathtoSuppr.size());
		ArrayList<PathREF> vPathREF_new = new ArrayList<PathREF>(vPathREF.size() - setPathtoSuppr.size());
		for (PathREF p : vPathREF)
			if (!setPathtoSuppr.contains(p))
				vPathREF_new.add(p);
			else
				// Le path sera supprimé, alors on met à jour le type de ses junctions
				updateJunctionOfPathSuppr(p);
				
		vPathREF.clear();
		vPathREF.addAll(vPathREF_new);
		

		// Maintenant que les Path ont été coupés ou supprimés, certaines JunctionREF, présentes
		//  dans le vecteur global vJunction sont à supprimer, d'où
		// Nettoyage du vecteur global vJunction : on ne garde que celle qui sont référencées
		//  au moins une fois dans un des path + les nodes (car il peut y avoir des nodes isolés)
		JunctionREF.cleanVJunctionREFGlobal(vPathREF, vJunctionREF);
		// Remarque : vNodeREF n'est pas à nettoyer car on n'a supprimé aucun node

		// Amélioration V20 13/04/2013 [FIN]
		// Re-compte des intersections
		recenseNodeAvecPathNonConnecte(vNodeREF, vPathREF, spiPath, false);
		// petit log des nodes avec augmentation des intersections
		for (JunctionREF node : mapNodePathNonConnecte_avant.keySet()) {
			int nbIntersect_avant = mapNodePathNonConnecte_avant.get(node);
			int nbIntersect_apres = -1;
			if (mapNodePathNonConnecte_apres.containsKey(node))
				nbIntersect_apres = mapNodePathNonConnecte_apres.get(node);
			if (nbIntersect_apres>nbIntersect_avant) {
				//System.out.println("{{{{{{{{{{ " + node + " : " + nbIntersect_avant + " -> " + nbIntersect_apres);
				if (setNodeMultipleIntersectAvecMemePath_apres.contains(node))
					Logger.log_WARN(node +  " : Node contenant potentiellement des intersections parasites apres simplification node");
			}
			else {
				if (setNodeMultipleIntersectAvecMemePath_apres.contains(node))
					Logger.log_WARN("?? " + node +  " : Node contenant potentiellement des intersections parasites apres simplification node");
			}
		}


//		for (JunctionREF node : setNodeMultipleIntersectAvecMemePath_apres) {
//			System.out.println("<><><><> " + node);
//		}
		
		clear();
	}

	// Détruit les objets éventuellement créés par ce module et supprime les liens statiques avec les objets passés en paramètres
	private static void clear() {
		vJunctionREF = null;
	}


	// Détermine et stocke pour chaque node son rayon de simplification souhaité
	static private void computeRayonSimplifNodes (Params_ls params, ArrayList<JunctionREF> vNodes, SimpleGridSPI spiPath, SimpleGridSPI spiNode) {
		largestRayon = 0;
		
		// on a besoin de ca, car on va traiter des groupes de nodes dont les ZS s'intersectent
		//  au sein du for global qui énumère tous les nodes
		THashSet<JunctionREF> setNodeComputed = new THashSet<JunctionREF>();
		
		for (JunctionREF node : vNodes) {
			
			if (setNodeComputed.contains(node)) continue;
			setNodeComputed.add(node);

			//********************************************************************
			// A) Calcul de la valeur par défaut, celle demandée par l'utilisateur
			
//			// cas special V18 // NOTION ABANDONNEE au profit d'un réglage au cas par cas (via les ZI) des nodes dans les params
//			if (node.nodeData.complexite_initiale <= params.seuilPourReductionDistSimplifNode) {
//				node.nodeData.k_correctif = 0;
//				node.nodeData.rayon_Simplification_m = 0;
//			} else {
			node.nodeData.k_correctif = 1.0; // pour l'instant du moins (valeur par defaut)
			// le rayon de simplif est un facteur du rayon
			node.nodeData.rayon_Simplification_m = node.getRayonNodeFinal() * node.nodeData.contrainteGeom.zs;
			largestRayon = Math.max(largestRayon, node.nodeData.rayon_Simplification_m);
//			}
			
			
			//********************************************************************
			// B) A present, on réduit les ZS s'il y a intersection de ZS
			
			// Récupération des nodes dont la ZS intersectent celle du node en cours
			THashSet<JunctionREF> setNodeIntersect = new THashSet<JunctionREF>();
			completeSetAvecNodesThatMayIntersectZS_rec(node, setNodeIntersect, spiNode);
			Logger.log_DEBUG2("  -> " + setNodeIntersect.size() + " nodes intersectent sa ZS");	

			if (setNodeIntersect.size()>1) {
				// On traite chaque node l'un après l'autre en prenant soin de réduire les ZS de sorte
				//  que pour un node donné, sa ZS n'englobe jamais un autre node du groupe
				ArrayList<JunctionREF> vNodeProche = new ArrayList<JunctionREF>(setNodeIntersect.size());
				vNodeProche.addAll(setNodeIntersect);

				int nbNodeProche = vNodeProche.size();
				for (int i=0; i<nbNodeProche; i++) {
					JunctionREF node_i = vNodeProche.get(i);
					JunctionREF node_closest = getClosestNode (node_i, vNodeProche);

					// Doit-on reduire la taille du rayon de simplif ?
					if (node_i.nodeData.rayon_Simplification_m > node_i.distTo(node_closest) * facteurReductionZSsiConflit) {
						double new_rayonSimplif = node_i.distTo(node_closest) * facteurReductionZSsiConflit;
						node_i.nodeData.k_correctif = new_rayonSimplif/node_i.nodeData.rayon_Simplification_m; // pour affichage debug
						node_i.nodeData.rayon_Simplification_m = new_rayonSimplif;
					}
				}
			}
				
			//********************************************************************
			// C) Amélioration v20 avril 2013 :
			//  avant : un bout de ligne partant du node et étant coupé par un encart, si cette coupure se situe dans la ZS
			//   alors tout le bout de ligne est supprimé alors qu'il gagnerait a etre visible
			//  mnt : on réduit la ZS de sorte que l'extrémité coupée soit hors de la ZS et ainsi le bout de ligne n'est pas supprimé
			// ex dans BV 12/2012 : PHALS
			// (dans certains cas, le node pouvait parraitre  completement isolé ! ex SSCOR)

			// Algo : on réduit la ZS si :
			//  - il existe dans la ZS un path connecté au node (directement ou non)
			//  - dont une seule des extrémité est STD
			//  - et située entre le rayon du node et la ZS
			//  - et issue d'un découpage sur un encart
			double newRayonZS = node.nodeData.rayon_Simplification_m;
			Rect_d zsNode = new Rect_d(node, node.nodeData.rayon_Simplification_m*2);
			ArrayList<PathREF> vPathProche = (ArrayList) spiPath.getObjectsThatMayIntersectWith(zsNode);
			for (PathREF path : vPathProche) {
				if (path.isConnectedToNode(node) &&	path.isOnlyOneExtrStd()) {
					// on a un path connecté au node et dont une seule des extrémité est STD
					JunctionREF jStd = (JunctionREF) (path.from().isStd() ? path.from() : path.to());
					if (jStd.zoneIfIntersectSegZone == null) continue;
					double distToNode = jStd.distTo(node);
					if (distToNode>node.diametre_reel_m && distToNode<=node.nodeData.rayon_Simplification_m) {
						// Cette extr SDT est entre le rayon du node et la ZS : si on ne fait rien, le path sera coupé et invisible
						//  alors qu'on aimerait bien voir le petit bout qui dépasse
						// -> on réduit le rayon de la ZS
						newRayonZS = Math.min(newRayonZS, 0.98 * distToNode);
						Logger.log_DEBUG1("Réduction du rayon de simplif du node " + node + " : " + node.nodeData.rayon_Simplification_m + " -> " + newRayonZS + " (cause : bout de ligne a conserver proche encart)");
					}
				}
			}
			// On remplace le rayon ZS par le nouveau calculé
			node.nodeData.rayon_Simplification_m = newRayonZS;
		}
	}
	

	// Renvoie le node le plus proche parmi ceux d'un vecteur
	private static JunctionREF getClosestNode(JunctionREF node, ArrayList<JunctionREF> vNodeProche) {
		double distClosest = Double.POSITIVE_INFINITY;
		JunctionREF nodeClosest = null;
		int nbNodeProche = vNodeProche.size();
		for (int i=0; i<nbNodeProche; i++) {
			JunctionREF node2 = vNodeProche.get(i);
			if (node==node2) continue;
			double dist = node.distTo(node2);
			if (dist<distClosest) {
				nodeClosest = node2;
				distClosest = dist;
			}
		}
		return nodeClosest;
	}
	
	
	// zsNode représente en fait un disque ...
	static private void simplifyNode(JunctionREF node, THashSet<PathREF> setPathToSuppr, SimpleGridSPI spiPath) {
		Logger.log_DEBUG2(" => Simplif du node : " + node);
		// Le node a peut etre deja ete simplifé car faisant partie d'un groupe de nodes proches
//		if (node.nodeData.bSimplified)
//			return;

		// Node qu'on ne veut pas simplifier
		if (node.nodeData.rayon_Simplification_m == 0) {
			node.nodeData.bSimplified = true;
			return;
		}

		node.nodeData.bSimplified = true;
		Rect_d zsNode = new Rect_d(node, node.nodeData.rayon_Simplification_m*2);
		
		// Reinitialisé à chaque node, sert a ne pas parcourir 2 fois les path lors de la recursion
		THashSet<PathREF> setPathDejaParcouru = new THashSet<PathREF>(32);
		
		
// La suite ne fonctionne pas car si l'un des path de la boucle est coupé durant la récursion
//  alors jExtr sera mise à jour et notamment se verra supprimer ce path !
// On doit donc alors stocker les path connectés dans un petit vecteur temporaire ...
//		int nbPath = node.getNbPathContenant();
//		for (int i=0; i<nbPath; i++) {
//			PathREF pathREF = (PathREF) node.getPathContenantNum(i);
//			parcoursPath_rec(pathREF, node, node, zsNode, setPathToSuppr, setPathDejaParcouru);
//		}
		int nbPath = node.getNbPathContenant();
		ArrayList<PathREF> vPath = new ArrayList<PathREF>(nbPath);
		
		for (int i=0; i<nbPath; i++)
			vPath.add((PathREF) node.getPathContenantNum(i));
		for (int i=0; i<nbPath; i++) {
			PathREF pathREF = vPath.get(i);
			parcoursPath_rec(pathREF, node, node, zsNode, setPathToSuppr, setPathDejaParcouru, false);
		}

		// Petit raffinement :
		// S'il y a des path isolés dans le rayon de simplification, ils n'auront pas été supprimés par ce qui précède
		//  car ils n'ont aucune connexion avec personne d'autre
		// Dans cette version, la détection de non connextion est simple (et non recursive) : il faut qu'au moins une extrémité soit STD
		ArrayList<PathREF> vPathProche = (ArrayList) spiPath.getObjectsThatMayIntersectWith(zsNode);
		for (PathREF path : vPathProche) {
			if (setPathToSuppr.contains(path)) continue; //deja traite
			if ( (path.from().isStd() || path.to().isStd()) &&
					path.areAllJunctionInCircle(node, node.nodeData.rayon_Simplification_m)) {
				Logger.log_DEBUG1("Path isolé et dans le rayon de simplification du node " + node + " -> supprimé : " + path);
				setPathToSuppr.add(path);
			}
		}
	}
	
	static private void parcoursPath_rec (PathREF path, JunctionREF jStart, JunctionREF node, Rect_d zsNode, THashSet<PathREF> setPathToSuppr, THashSet<PathREF> setPathDejaParcouru, boolean bForceEndRecursion) {
// Depuis V20 01/05/2013, on ne fait plus ce test, car on peut avoir ZS > ZC : on laisse plus de liberté de paramétrage
//		assert (path.isConnectedToNode(node));
		assert (jStart.isExtremite(path));
		assert (jStart.distTo(node)<node.nodeData.rayon_Simplification_m);
		
		if (setPathDejaParcouru.contains(path))
			// on a deja parcouru ce path lors de la récursion
			return;
		setPathDejaParcouru.add(path);
		
		// On parcourt le path à l'envers (de l'intérieur de la ZS vers l'extérieur),
		//  pour le couper plus facilement si intersection avec la ZS (cf. cutPath())
		//  (et pour simplifier le parcours, si le path est à l'endroit, on le retourne)
		if (jStart == path.from())
			path.reverse();
		assert (jStart==path.to());
		
		ArrayList<JunctionREF> vJ = path.getVJunctionREF();
		int nbJunction = vJ.size();
		for (int i=nbJunction-1; i>=0; i--) {
			JunctionREF junct = vJ.get(i);
	
			double distToNode = junct.distTo(node);
			
			if (distToNode < node.nodeData.rayon_Simplification_m) {
				// La junction est encore à l'intérieur de la ZS
				// Si c'est une intersection, on teste si des path ont pour extrémité cette intersect et appel récursif
				// => en fait NON, car a ce stade du traitement, les intersections sont forcéments
				//  vraies = que des path traversant
				//  -> les nouveaux path partent forcément d'une extrémité du path en cours
				if (junct.isIntersect())
					assert(junct.getVPathContenant_nonTraversant().size()==0);
				
			} else {
				// On est sur la 1ere junction qui sort de la ZS
				
				Point_d ptI;
				JunctionREF jI;

				// Cas limite le point est pile poil sur la limite de la ZS
				if (Math.abs(distToNode - node.nodeData.rayon_Simplification_m) < 0.0001) {
					Logger.log_DEBUG2("!!!!! CAS LIMITE : path " + path + " au niveau du node " + node);
					jI = junct;
				} else {

					// Le path est strictement à cheval sur la ZS du node -> on doit le couper
					// -> on determine l'intersection entre la ZS du node et le dernier segment parcouru du path
					Segment_d seg = new Segment_d(junct, vJ.get(i+1));
					int nbIntersect = UtilsGeom.computeIntersect_Ellipse_Segment(zsNode, seg, tabCoord);


					if (nbIntersect==1) {
						// Cas normal ...
						ptI = new Point_d(tabCoord[0], tabCoord[1]);
						jI = new JunctionREF(ptI, TypeJunction.STD, path);
						jI.addPathContenant(path);
					} else if (nbIntersect == 0) {
						Logger.log_WARN("Intersection vide lors du découpage du path " + path + " au niveau du node " + node);
						// On est dans un cas limite : junc doit être pile sur le bord de la ZS
						//  on met jI a null, car l'intersect EST 'junct'
						// mais c'est bizare, on aurait du detecter ce cas dans le 'if' ci-avant
						// il s'agit en fait d'une erreur d'arrondi infime, mais sur un seuil un peu plus grand que le seuil
						//  utilisé dans le 'if'
						jI = null;
					} else { // nbIntersect = 2
						Logger.log_WARN("Double intersection lors du découpage du path " + path + " au niveau du node " + node);
						// bizarre autant qu'étrange (bug ?) mais tant  pis, on prend la 1ere intersection ...
						ptI = new Point_d(tabCoord[0], tabCoord[1]);
						jI = new JunctionREF(ptI, TypeJunction.STD);
						jI.addPathContenant(path);
					}
				}
				
				// Si le point d'intersection trouvé est précisément le 1er point hors de la ZS
				//  (ie. ce point est situé pile sur la frontière de la ZS ...)
				// alors on le rend null, cutPath() ne l'ajoutera alors pas, sinon ca ferait un doublon
				if (jI!= null && jI.equalsXY(junct))
					jI = null;
				
				if (jI != null) {
					// nouvelle junction -> on doit l'ajouter au vecteur global
					vJunctionREF.add(jI);
				}
				
				// 19/06/2014 : enfin le bon traitement pour les path revenant dans la ZS
				// suite au bug du 12/11/2012, on avait arreté le traitement de ces path
				//  mais il restait un bug dans certains cas (cf. log 2014)
				if (path.from().distTo(node) < node.nodeData.rayon_Simplification_m &&
						!path.hasBeenCutDuringSimplifNode()) {
					
					setPathDejaParcouru.remove(path);
				}
				
				// Coupure effective
				cutPath(path, i, jI, node);

//				// Cas special si le path revient dans la ZS, on rappelle le traitement
//				//  dans l'autre sens (en prenant soin de stopper de la récursion)
//				if (!bForceEndRecursion && path.from().distTo(node) < node.nodeData.rayon_Simplification_m) {
//					setPathDejaParcouru.remove(path);
//					parcoursPath_rec(path, (JunctionREF)path.from(), node, zsNode, setPathToSuppr, setPathDejaParcouru, true);
//					
//				}
// BUG decouvert le 12/11/2012
// comme le path est revenu dans la ZS, il faudrait poursuivre la recursion !!
// or la on return !!
// 13/11/2012 : on supprime cette subtilité : si path sort puis re-rentre, alors on considère qu'il est sorti
				
				
				
				
				return; // on arrete le parcours des junctions du path et on arrete la récursion
				
			}
			
		}
		
		// Si on arrive ici, c'est qu'on n'est jamais sorti de la ZS pdt le parcours, ou que le path est ressorti et revenu dans la ZS
		// -> le path est complètement à l'intérieur de la ZS
		setPathToSuppr.add(path);
		
		// On a souhaité traiter un unique path
		if (bForceEndRecursion)
			return;


		//******** Parcours récursif à partir de l'extrémité du path **************
		JunctionREF jExtr = (JunctionREF) path.from();
		
		if (jExtr == node)
			return; // la recursion au niveau du node est déjà gérée au niveau de simplifyNode()
		// ce test est utile suite à correction 19/06/2014, dans laquelle, si un path revient dans le node, on choisit
		//  de le desinscrire comme traité, pour qu'il soit éventuellement coupé à son autre extrémité plus tard dans la
		//  recursion. donc, si on ne stoppe pas la recursion net au niveau du node, on pourrait reparcourir
		//  indefiniment ce path par le bout deja coupé ...

// La suite ne fonctionne pas car si l'un des path de la boucle est coupé durant la récursion
//  alors jExtr sera mise à jour et notamment se verra supprimer ce path !
// On doit donc alors stocker les path connectés dans un petit vecteur temporaire ...
//		int nbPathExtr = jExtr.getNbPathContenant();
//		for (int i=0; i<nbPathExtr; i++) {
//			PathREF newPath = (PathREF) jExtr.getPathContenantNum(i);
//			if (newPath != path && newPath.isConnectedToNode(node))
//				parcoursPath_rec(newPath, jExtr, node, zsNode, setPathToSuppr, setPathDejaParcouru);
//		}
		
		int nbPathExtr = jExtr.getNbPathContenant();
		ArrayList<PathREF> vPath = new ArrayList<PathREF>(nbPathExtr);
		for (int i=0; i<nbPathExtr; i++)
			vPath.add((PathREF) jExtr.getPathContenantNum(i));

		for (int i=0; i<nbPathExtr; i++) {
			PathREF newPath = vPath.get(i);
			// Depuis V20 01/05/2013, on ne fait plus le test isConnected(), car on peut avoir ZS > ZC : on laisse plus de liberté de paramétrage
			if (newPath != path /*&& newPath.isConnectedToNode(node)*/ && jExtr.isExtremite(newPath)) // il faut etre extremité pour le cas des INTERSECT
				parcoursPath_rec(newPath, jExtr, node, zsNode, setPathToSuppr, setPathDejaParcouru, false);
		}
	}
	
	
	
	// Coupe le path à partir de la junction suivant 'indexLastToKeep', puis rajoute jI si non null, puis rajoute node
	static private void cutPath(PathREF path, int indexLastToKeep, JunctionREF jI, JunctionREF node) {
		path.setIsCutDuringSimplifNode();
		
		ArrayList<JunctionREF> vJ = path.getVJunctionREF();
		int nbJunctToSuppr = vJ.size() - indexLastToKeep - 1;
		for (int j=0; j<nbJunctToSuppr; j++) {
			JunctionREF jSuppr = vJ.remove(vJ.size()-1); // en s'y prenant par la fin, on évite des copies de données lourde
			updateTypeJunctionBecausePathCutOrSuppr(jSuppr, path);
		}

		if (jI != null)
			vJ.add(jI);
		
		vJ.add(node);
		// ci avant, on a appelé updateTypeJunctionBecausePathCutOrSuppr() sur ce node si le path etait deja connecté au node
		// ou bien, le path n'était pas directement connecté au node avant
		// donc il faut déclarer le node (a nouveau ?) comme connecté au path
		node.addPathContenant(path);
	}
	
	
	
	// Complete le set avec l'ensemble des nodes dont la ZS intersecte la ZS du node fourni
	//  de façon récursive.
	// Lors de l'appel, le set ne doit pas deja contenir node
	static private void completeSetAvecNodesThatMayIntersectZS_rec(JunctionREF node, THashSet<JunctionREF> setNodeIntersect, SimpleGridSPI spiNode) {
		setNodeIntersect.add(node);
		
		ArrayList<JunctionREF> vNodeIntersect = getNodesThatMayIntersectZS(node, spiNode);
		if (vNodeIntersect==null || vNodeIntersect.isEmpty())
			return;
		for (JunctionREF nodeIntersect : vNodeIntersect) {
			if (setNodeIntersect.contains(nodeIntersect))
				continue; // fin de la recursion
			else
				completeSetAvecNodesThatMayIntersectZS_rec(nodeIntersect, setNodeIntersect, spiNode);
		}
	}
		
	// Renvoie les nodes dont la ZS intersectent la ZS du node fourni
	// Variable pour accélérer traitement
	static private Rect_d rectZoneSimplif__ = new Rect_d();
	static private ArrayList<JunctionREF> getNodesThatMayIntersectZS(JunctionREF node, SimpleGridSPI spiNode) {
		ArrayList<JunctionREF> vNodeIntersect = new ArrayList<JunctionREF>(5);
		rectZoneSimplif__.setCoord(node.x, node.y, node.nodeData.rayon_Simplification_m + largestRayon);

		ArrayList<JunctionREF> vNodeProche = (ArrayList) spiNode.getObjectsThatMayIntersectWith(rectZoneSimplif__);
		int nbNodeProche = vNodeProche.size();
		for (int i=0; i<nbNodeProche; i++) {
			JunctionREF nodeProche = vNodeProche.get(i);
			if (nodeProche==node) continue;
			
			if (node.distTo(nodeProche) <= (node.nodeData.rayon_Simplification_m + nodeProche.nodeData.rayon_Simplification_m)) {
				// Les 2 nodes sont suffisement proches
				
				if (vNodeIntersect==null)
					// on ne crée le vecteur que s'il y a reellement au moins intersection
					//  cela optimise le trt car il y aura le plus souvent AUCUNE intersection
					vNodeIntersect = new ArrayList<JunctionREF>();
				
				vNodeIntersect.add(nodeProche);
			}
		}
		return vNodeIntersect;
	}

	
	
	// Path allant etre supprimé, on met à jour le type de toutes ses junctions
	private static void updateJunctionOfPathSuppr(PathREF path) {
		ArrayList<JunctionREF> vJ = path.getVJunctionREF();
		int nbJ = vJ.size();

		for (int i=0; i<nbJ; i++)
			updateTypeJunctionBecausePathCutOrSuppr(vJ.get(i), path);
	}


	
	// Lorsque qu'une junction fait partie d'un path coupé ou supprimé, 
	// alors si c'est une junction multiple, elle ne sera pas supprimée mais il faut changer son type
	// (si c'est une junction standard, elle sera supprimée en post-traitement, donc peu importe son type)
	static private void updateTypeJunctionBecausePathCutOrSuppr (JunctionREF j, PathREF path) {
		j.removePathContenant(path);
		j.majTypeJunction();
	}
	
	
	//**************************************************************************
	//**************************************************************************
	
	// Amélioration V20 13/04/2013
	// On recense les nodes qui risquent d'avoir des intersections parasites entre les path connecté au node (et simplifiés en ligne droite)
	//  et les path non connectés au poste qui passent tout près
	
	static private HashMap<JunctionREF, Integer> mapNodePathNonConnecte_avant;
	static private HashMap<JunctionREF, Integer> mapNodePathNonConnecte_apres;
	static private HashSet<JunctionREF> setNodeMultipleIntersectAvecMemePath_apres;
	
	static private void recenseNodeAvecPathNonConnecte(ArrayList<JunctionREF> vNodeREF, ArrayList<PathREF> vPathREF, SimpleGridSPI spiPath, boolean bAvant) {
		spiPath.clear();
		spiPath.addObjects (vPathREF);
		
		// La map ou on stocke les resultats depend si on est en debut ou en fin de traitement
		HashMap<JunctionREF, Integer> mapNodePathNonConnecte;
		
		if (bAvant){
			mapNodePathNonConnecte_avant = new HashMap<JunctionREF, Integer>();
			mapNodePathNonConnecte = mapNodePathNonConnecte_avant;
		} else {
			mapNodePathNonConnecte_apres = new HashMap<JunctionREF, Integer>();
			mapNodePathNonConnecte = mapNodePathNonConnecte_apres;
			
			setNodeMultipleIntersectAvecMemePath_apres = new HashSet<JunctionREF>();
		}
		
		// La liste des nodes a traiter depend si on est en debut ou en fin de traitement
		Collection<JunctionREF> nodeATraiter;
		if (bAvant)
			nodeATraiter = vNodeREF;
		else
			// en fin de traitement, on ne recompte les intersections qu'avec les nodes identifiés en debut de traitement
			//  ca va plus vite
			nodeATraiter = mapNodePathNonConnecte_avant.keySet();
		
		// Parcours des nodes
		for (JunctionREF node : nodeATraiter) {
			double rayonSimplif = node.nodeData.rayon_Simplification_m; 
			
			// Recherche des path dans sa zone de simplif
			Rect_d rectRecherche = new Rect_d(node, rayonSimplif*2);
			ArrayList<PathREF> vPathProche = (ArrayList) spiPath.getObjectsThatMayIntersectWith(rectRecherche);
			
			// 2 listes de path : ceux connectés et ceux non connectés
			ArrayList<PathREF> vPathDansZS_connected = new ArrayList<PathREF>();
			ArrayList<PathREF> vPathDansZS_notConnected = new ArrayList<PathREF>();
					
			// On détermine si les path proches (dans rect de recherche)  intersecte vraiment la ZS qui est un disque
			for (PathREF path : vPathProche) {
				
				if (path.doesIntersectCircle(node, rayonSimplif)) {
					if (path.isConnectedToNode(node))
						vPathDansZS_connected.add(path);
					else
						vPathDansZS_notConnected.add(path);
				}
			}
			
			// S'il existe des path non connectés et dans la ZS (sinon, il n'y a rien a faire)
			if (vPathDansZS_notConnected.size()>0) {
				
				int nbIntersectTotal = 0;
				
				// On compte le nombre d'intersection entre les path non connecté et ceux connectés
				// si apres traitement, un path non connecté intersecte 2 fois un path connecté (qui dona été simplifié en ligne droite
				//  puisque qu'on se limite à la ZS pour la recherche d'intersection), alors
				//  le node est tres douteux, et on le stocke dans setNodeMultipleIntersectAvecMemePath_apres
				for (PathREF path_notConnected : vPathDansZS_notConnected) {
					for (PathREF path_connected : vPathDansZS_connected) {
						int nbIntersect = A_Path.getNbIntersectionBetween2Path(path_notConnected, path_connected, node, rayonSimplif);
						nbIntersectTotal += nbIntersect;
						if (!bAvant && nbIntersect>=2)
							setNodeMultipleIntersectAvecMemePath_apres.add(node);
					}
				}
				
				mapNodePathNonConnecte.put(node, nbIntersectTotal);
			}
			
		}
	}
}
